using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.Xml.Serialization;
using System.Xml;
using System.IO;
using DynamiteXml.HelperLogic;

namespace DynamiteXml
{
    /// <summary>
    /// Static class that provides services to serialize and deserialize an IDynamiteXml.
    /// </summary>
    public static class DynamiteXmlLogic
    {
        /// <summary>
        /// Service to serialize an IDynamiteXml implementing object.
        /// Use the Deserialize method of this class to get the IDynamiteXml back.
        /// </summary>
        /// <param name="DynamiteXml">
        /// The IDynamiteXml that needs to be serialized.
        /// </param>
        /// <returns>
        /// The xml string representing the container that holds the IDynamiteXml and it's type identifier.
        /// </returns>
        public static string Serialize(IDynamiteXml DynamiteXml)
        {
            string result;

            Container container = new Container();
            container.Xml = DynamiteXml;

            StringBuilder stringBuilder = new StringBuilder();

            XmlSafeTextLogic.EnforceXmlSafeText(DynamiteXml);
            using (XmlWriter writer = XmlWriter.Create(stringBuilder))
            {
                XmlSerializer xmlSerializer = new XmlSerializer(container.GetType());
                xmlSerializer.Serialize(writer, container);
            }
            result = stringBuilder.ToString();

            return result;
        }

        /// <summary>
        /// Service to deserialize an IDynamiteXml which has been serialized with the Serialize method of this class.
        /// </summary>
        /// <param name="wrappedXml">
        /// The xml string representing the container that holds the IDynamiteXml and it's type identifier.
        /// </param>
        /// <returns>
        /// The IDynamiteXml that has been serialized previously.
        /// </returns>
        public static IDynamiteXml Deserialize(string wrappedXml)
        {
            IDynamiteXml result;
            Container container;

            StringReader stringReader = new StringReader(wrappedXml);
            using (XmlReader reader = XmlReader.Create(stringReader))
            {
                XmlSerializer xmlSerializer = new XmlSerializer(typeof(Container));
                container = (Container)xmlSerializer.Deserialize(reader);
            }

            result = container.Xml;

            return result;
        }

        /// <summary>
        /// Service to deserialize an IDynamiteXml which has been serialized with the Serialize method of this class.
        /// </summary>
        /// <typeparam name="TXml">
        /// The type to which the IDynamiteXml needs to be casted.
        /// A InvalidCastException will be thrown when when the cast fails.
        /// </typeparam>
        /// <param name="wrappedXml">
        /// The xml string representing the container that holds the IDynamiteXml and it's type identifier.
        /// </param>
        /// <returns>
        /// The TXml that has been serialized previously.
        /// </returns>
        public static TXml Deserialize<TXml>(string wrappedXml) where TXml : class, IDynamiteXml
        {
            return Deserialize<TXml>(wrappedXml, false);
        }

        /// <summary>
        /// Service to deserialize an IDynamiteXml which has been serialized with the SerializeDynamiteXml method of this class.
        /// </summary>
        /// <typeparam name="TXml">
        /// The type to which the IDynamiteXml needs to be casted.
        /// A InvalidCastException will be thrown when when the cast fails.
        /// </typeparam>
        /// <param name="wrappedXml">
        /// The xml string representing the container that holds the IDynamiteXml and it's type identifier.
        /// </param>
        /// Applicable in case the casting fails. When true, null is returned. Else an InvalidCastException is thrown.
        /// <param name="allowNull">
        /// </param>
        /// <returns>
        /// The TXml that has been serialized previously.
        /// </returns>
        public static TXml Deserialize<TXml>(string wrappedXml, bool allowNull) where TXml : class, IDynamiteXml
        {
            TXml result;

            IDynamiteXml xml = Deserialize(wrappedXml);
            if (xml is TXml)
            {
                result = (TXml)xml;
            }
            else if (allowNull)
            {
                result = null;
            }
            else
            {
                throw new InvalidCastException(string.Format("Cast to type {0} failed.", typeof(TXml)));
            }

            return result;
        }
    }
}
